﻿/**
 * 点类的构造方法。
 * @param x, y  坐标
 */
function Point(x, y) {
    this.x = x
    this.y = y

    /**
     * 判断两点是否相等。
     * @param point 点
     */
    this.equals = function(point) {
        return this.x == point.x && this.y == point.y
    }
}

/**
 * 蛇类的构造方法。
 * @param drawer    绘图者
 */
function Snake(drawer) {
    var mCauda = null                   // 蛇尾
    var mDirection = Const.KEY_DOWN     // 移动方向
    var mHead = null                    // 蛇头

    // 初始化成员
    const caudaPos = new Point(0, 0)
    mHead = mCauda = new SnakeBody(drawer, caudaPos, SnakeBodyType.SNAKE)

    /**
     * 吃果子，即将果子放到蛇尾。
     * @param apple 果子
     */
    this.eat = function(apple) {
        // 设置吃到的果子的位置
        const position = new Point(mHead.getPosition().x, mHead.getPosition().y)
        switch (mDirection) {
            case Const.KEY_DOWN:
                position.y++
                break 
            case Const.KEY_LEFT:
                position.x--
                break 
            case Const.KEY_RIGHT:
                position.x++
                break 
            case Const.KEY_UP:
                position.y--
                break 
        }
        apple.setPosition(position)

        // 改变果子的类型
        apple.setType(SnakeBodyType.SNAKE)

        // 重绘蛇头和果子
        mHead.draw()
        apple.draw()
        
        // 将吃到的果子变成蛇头
        mHead.setNext(apple)
        mHead = apple
    }

    /**
     * 获取蛇头。
     * @return 蛇头
     */
    this.getHead = function() {
        return mHead
    }

    /**
     * 移动。
     * @return 蛇头碰到自身或游戏区域边界则返回 true，否则返回 false
     */
    this.move = function() {
        // 计算蛇尾的新位置并抹除蛇尾
        const headPositon = mHead.getPosition()
        const point = new Point(headPositon.x, headPositon.y)
        switch (mDirection) {
            case Const.KEY_DOWN:
                if (point.y + 1 >= Const.GAME_BOARD_GRID_COUNT_V) {
                    // 如果蛇头超出游戏区域下边界，则返回
                    return true
                }
                mCauda.erase()
                point.y++
                break 
            case Const.KEY_LEFT:
                if (point.x - 1 < 0) {
                    // 如果蛇头超出游戏区域左边界，则返回
                    return true
                }
                mCauda.erase()
                point.x--
                break 
            case Const.KEY_RIGHT:
                if (point.x + 1 >= Const.GAME_BOARD_GRID_COUNT_H) {
                    // 如果蛇头超出游戏区域右边界，则返回
                    return true
                }
                mCauda.erase()
                point.x++
                break 
            case Const.KEY_UP:
                if (point.y - 1 < 0) {
                    // 如果蛇头超出游戏区域上边界，则返回
                    return true
                }
                mCauda.erase()
                point.y--
                break
        }
        mCauda.setPosition(point)

        // 将蛇尾的下一个结点设置为蛇尾、原蛇尾设置成蛇头
        mHead.setNext(mCauda)
        mHead = mCauda
        mCauda = mCauda.getNext()
        mHead.setNext(null)
        mHead.draw()

        // 检查蛇头是否碰到蛇身
        for (var snakeBody = mCauda; snakeBody != mHead; snakeBody = snakeBody.getNext()) {
            if (snakeBody.getPosition().equals(mHead.getPosition())) {
                return true
            }
        }

        return false
    }

    /**
     * 检查某点是否与蛇身位置重叠。
     * @param point 被检查的点
     * @return 点与蛇身位置重叠则返回 true ，否则返回 false
     */
    this.pointIsOnMe = function(point) {
        for (var snakeBody = mCauda; snakeBody != null; snakeBody = snakeBody.getNext()) {
            if (snakeBody.getPosition().equals(point)) {
                return true
            }
        }
        return false
    }

    /**
     * 设置蛇的颜色。
     * @param color 颜色
     */
    this.setColor = function(color) {
        mColor = color
        for (var snakeBody = mCauda; snakeBody != null; snakeBody = snakeBody.getNext()) {
            snakeBody.setColor(color)
            snakeBody.draw()
        }
    }

    /**
     * 转弯。
     * @param direction 方向
     */
    this.turn = function(direction) {
        // 如果方向与当前方向相反（想要直接调头），则直接返回
        if (Math.abs(direction - mDirection) == 2) {
            return
        }
        mDirection = direction
    }
}
